using System;
using System.Xml.XPath;
using System.Collections.Generic;
using System.Text;

using AIFSDataAccessLayer.Utilities;

namespace AIFSContentManagementSystem.DomainObjects.Mapper
{
    /// <summary>
    /// Helper class to Serialize Criteria to/from xml
    /// </summary>
    public sealed class CriteriaMapper : DtoMapper<Criteria>
    {
        ICriterionExpressionFactory expressionFactory;

        public CriteriaMapper(ICriterionExpressionFactory ExpressionFactory)
        {
            expressionFactory = ExpressionFactory;
        }

        /// <summary>
        /// Returns the Xml representation of the criteria expression in the following format
        /// <code>
        ///     <rules>
        ///         <rule name="COUNTRY" type="STRING" operator="=" value="US"/>
        ///         <rule name="POLICY_YEAR" type="NUMERIC" operator=">=" value="2011"/>
        ///     </rules>
        /// </code>
        /// </summary>
        public string SerializeToXml(Criteria articleCriteria)
        {
            if (articleCriteria == null)
                throw new AIFSContentManagementSystem.Exceptions.AIFSContentManagementSystemException("Article Criteria is required", new ArgumentNullException("articleCriteria"));

            StringBuilder sbr = new StringBuilder();
            sbr.Append("<rules>");

            foreach(CriterionExpression expression in articleCriteria.CriteriaExpression)
            {
                if (string.IsNullOrEmpty(expression.RightOperand) || string.IsNullOrEmpty(expression.Oper) || string.IsNullOrEmpty(expression.LeftOperand))
                    throw new AIFSContentManagementSystem.Exceptions.AIFSContentManagementSystemException("Article criteria does not contain either the right operand, the operator or the left operand");

                sbr.AppendFormat("<rule name=\"{0}\" type=\"{1}\" operator=\"{2}\" value=\"{3}\"/>", expression.LeftOperand, expression.DataType, expression.Oper, expression.RightOperand);
            }

            sbr.Append("</rules>");

            return sbr.ToString();
        }
        
        private List<CriterionExpression> Map(string articleCriteriaXml)
        {
            try
            {
                XPathDocument rulesPath = new XPathDocument(new System.IO.StringReader(articleCriteriaXml));
                XPathNavigator rulesNavigator = rulesPath.CreateNavigator();
                XPathNodeIterator ruleIterator = rulesNavigator.Select("/rules/rule");

                List<CriterionExpression> expressionList = new List<CriterionExpression>();
                while (ruleIterator.MoveNext())
                {
                    XPathNavigator ruleNavigator = ruleIterator.Current;

                    string leftOperand = ruleNavigator.GetAttribute("name", string.Empty);
                    string dataType = ruleNavigator.GetAttribute("type", string.Empty);
                    string oper = ruleNavigator.GetAttribute("operator", string.Empty);
                    string rightOperand = ruleNavigator.GetAttribute("value", string.Empty);

                    //Defult data type to STRING
                    if (string.IsNullOrEmpty(dataType))
                        dataType = "STRING";

                    if (string.IsNullOrEmpty(rightOperand) || string.IsNullOrEmpty(dataType) || string.IsNullOrEmpty(oper) || string.IsNullOrEmpty(leftOperand))
                        throw new AIFSContentManagementSystem.Exceptions.AIFSContentManagementSystemException("Xml does not contain either the right operand, the operator, the left operand or the data type");

                    CriterionExpression expression = this.expressionFactory.CreateInstance(dataType);
                    expression.LeftOperand = leftOperand;
                    expression.Oper = oper;
                    expression.RightOperand = rightOperand;

                    expressionList.Add(expression);
                }

                return expressionList;
            }
            catch (AIFSContentManagementSystem.Exceptions.AIFSContentManagementSystemException aex)
            {
                throw aex;
            }
            catch (Exception ex)
            {
                throw new AIFSContentManagementSystem.Exceptions.AIFSContentManagementSystemException("Could not serealize to object", ex);
            }
        }

        internal List<CriterionExpression> Map(string[] StringExpression)
        {
            try
            {
                List<CriterionExpression> expressionList = new List<CriterionExpression>();
                for(int i = 0; i < StringExpression.Length;)
                {
                    string leftOperand = StringExpression[i++];
                    string dataType = StringExpression[i++];
                    string oper = StringExpression[i++];
                    string rightOperand = StringExpression[i++];

                    //Default data type to STRING
                    if (string.IsNullOrEmpty(dataType))
                        dataType = "STRING";

                    if (string.IsNullOrEmpty(rightOperand) || string.IsNullOrEmpty(dataType) || string.IsNullOrEmpty(oper) || string.IsNullOrEmpty(leftOperand))
                        throw new AIFSContentManagementSystem.Exceptions.AIFSContentManagementSystemException("Xml does not contain either the right operand, the operator, the left operand or the data type");

                    CriterionExpression expression = this.expressionFactory.CreateInstance(dataType);
                    expression.LeftOperand = leftOperand;
                    expression.Oper = oper;
                    expression.RightOperand = rightOperand;

                    expressionList.Add(expression);
                }

                return expressionList;
            }
            catch (AIFSContentManagementSystem.Exceptions.AIFSContentManagementSystemException aex)
            {
                throw aex;
            }
            catch (Exception ex)
            {
                throw new AIFSContentManagementSystem.Exceptions.AIFSContentManagementSystemException("Could not serealize to object", ex);
            }
        }

        protected override void Map(System.Data.DataRow drEntity, Criteria dto)
        {
            string criteriaXml = DataTableUtil.ToStringType(drEntity, "CRITERIA_TEXT");

            dto.CriteriaId = DataTableUtil.ToIntType(drEntity, "CRITERIA_ID");
            dto.CriteriaExpression = this.Map(criteriaXml);
        }
    }
}
